/*
  Copyright 2007 Anders Båtstrand

      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
      You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

      Unless required by applicable law or agreed to in writing, software
      distributed under the License is distributed on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      See the License for the specific language governing permissions and
      limitations under the License.

 */

package org.dyndns.anderlin.jmplayer.client.ui;

import org.dyndns.anderlin.jmplayer.model.Album;
import org.dyndns.anderlin.jmplayer.model.Track;
import org.dyndns.anderlin.jmplayer.service.AlbumService;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.binding.form.HierarchicalFormModel;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.richclient.application.PageComponentContext;
import org.springframework.richclient.application.event.LifecycleApplicationEvent;
import org.springframework.richclient.application.support.AbstractView;
import org.springframework.richclient.command.CommandGroup;
import org.springframework.richclient.command.support.AbstractActionCommandExecutor;
import org.springframework.richclient.command.support.GlobalCommandIds;
import org.springframework.richclient.dialog.CompositeDialogPage;
import org.springframework.richclient.dialog.ConfirmationDialog;
import org.springframework.richclient.dialog.TabbedDialogPage;
import org.springframework.richclient.dialog.TitledPageApplicationDialog;
import org.springframework.richclient.form.FormModelHelper;
import org.springframework.richclient.progress.TreeStatusBarUpdater;
import org.springframework.richclient.tree.FocusableTreeCellRenderer;
import org.springframework.richclient.util.PopupMenuMouseListener;
import org.springframework.util.Assert;

import javax.swing.*;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Collection;

public class AlbumManagerView extends AbstractView implements InitializingBean, ApplicationListener {

    private JTree albumTree;

    private DefaultTreeModel albumTreeModel;

    private AlbumService albumService;

    private DeleteExecutor deleteExecutor = new DeleteExecutor();

    private PropertiesExecutor propertiesExecutor = new PropertiesExecutor();

    private AlbumPropertiesExecutor albumPropertiesExecutor = new AlbumPropertiesExecutor();

    public void setAlbumService(AlbumService albumService) {
        this.albumService = albumService;
    }

    public void afterPropertiesSet() throws Exception {
        Assert.notNull(albumService, "A albumService must be provided.");
    }

    protected JComponent createControl() {
        JPanel view = new JPanel(new BorderLayout());
        createAlbumManagerTree();
        JScrollPane sp = new JScrollPane(albumTree);
        view.add(sp, BorderLayout.CENTER);
        return view;
    }

    private void createAlbumManagerTree() {
        DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode("Albums");
        Collection<Album> albums = albumService.getAlbums();
        for (Album album : albums) {
            DefaultMutableTreeNode albumNode = new DefaultMutableTreeNode(album);
            for (Track track : album.getTracks()) {
                DefaultMutableTreeNode trackNode = new DefaultMutableTreeNode(track);
                albumNode.add(trackNode);
            }
            rootNode.add(albumNode);
        }
        this.albumTreeModel = new DefaultTreeModel(rootNode);
        this.albumTree = new JTree(albumTreeModel);
        albumTree.setShowsRootHandles(true);
        albumTree.addTreeSelectionListener(new TreeStatusBarUpdater(getStatusBar()) {
            public String getSelectedObjectName() {
                Album selectedAlbum = getSelectedAlbum();
                if (selectedAlbum != null) {
                    return selectedAlbum.getTitle();
                }
                return "Albums";
            }
        });
        albumTree.addTreeSelectionListener(new TreeSelectionListener() {
            public void valueChanged(TreeSelectionEvent e) {
                updateCommands();

                // hour-glass while fetching
                albumTree.setCursor(new Cursor(Cursor.WAIT_CURSOR));
                /**
                 // fetch children of the selected object
                 if (getSelectedAlbum() != null) {
                 Album p = getSelectedAlbum();
                 DefaultMutableTreeNode n = getSelectedAlbumNode();
                 for (Image i : p.getImages()) {
                 n.add(new DefaultMutableTreeNode(i));
                 }
                 } else if (getSelectedImage() != null) {
                 Image p = getSelectedImage();
                 DefaultMutableTreeNode n = getSelectedImageNode();
                 for (ImageVersion i : p.getVersions()) {
                 n.add(new DefaultMutableTreeNode(i));
                 }
                 }    **/
                // finnished
                albumTree.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
            }
        });
        albumTree.addMouseListener(new PopupMenuMouseListener() {
            protected boolean onAboutToShow(MouseEvent e) {
                return !isRootOrNothingSelected();
            }

            protected JPopupMenu getPopupMenu() {
                return getSelectedAlbum() != null ? createAlbumPopupContextMenu() : null;
            }
        });
        albumTree.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {
                if (e.getClickCount() == 2 && e.getButton() == MouseEvent.BUTTON1 && propertiesExecutor.isEnabled()) {
                    propertiesExecutor.execute();
                }
            }
        });
        albumTree.setCellRenderer(getTreeCellRenderer());
        albumTree.setRootVisible(true);
    }

    private void updateCommands() {
        int treeSelectionCount = albumTree.getSelectionCount();
        if (isRootOrNothingSelected()) {
            deleteExecutor.setEnabled(false);
            propertiesExecutor.setEnabled(false);
        } else if (treeSelectionCount == 1) {
            deleteExecutor.setEnabled(true);
            propertiesExecutor.setEnabled(true);
        } else if (treeSelectionCount > 1) {
            deleteExecutor.setEnabled(true);
            propertiesExecutor.setEnabled(false);
        }
    }

    private boolean isRootOrNothingSelected() {
        return albumTree.getSelectionCount() == 0
                || (albumTree.getSelectionCount() == 1 && albumTree.isRowSelected(0));
    }

    private Album getSelectedAlbum() {
        DefaultMutableTreeNode node = getSelectedAlbumNode();
        if (node != null)
            return (Album) node.getUserObject();

        return null;
    }

    private DefaultMutableTreeNode getSelectedAlbumNode() {
        DefaultMutableTreeNode node = (DefaultMutableTreeNode) albumTree.getLastSelectedPathComponent();
        if (node == null || !(node.getUserObject() instanceof Album))
            return null;

        return node;
    }

    private DefaultTreeCellRenderer treeCellRenderer = new FocusableTreeCellRenderer() {
        public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded,
                                                      boolean leaf, int row, boolean hasFocus) {
            super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
            DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
            if (node.isRoot()) {
                this.setIcon(getIconSource().getIcon("folder.icon"));
            } else if (node.getUserObject() instanceof Album) {
                Album album = (Album) node.getUserObject();
                this.setText(album.getTitle());
                this.setIcon(getIconSource().getIcon("album.bullet"));
            }
            return this;
        }
    };

    public TreeCellRenderer getTreeCellRenderer() {
        return treeCellRenderer;
    }

    protected void registerLocalCommandExecutors(PageComponentContext context) {
        context.register(GlobalCommandIds.DELETE, deleteExecutor);
        context.register(GlobalCommandIds.PROPERTIES, propertiesExecutor);
    }

    /**
     * Handle an application event.
     *
     * @param e the event to respond to
     */

    public void onApplicationEvent(ApplicationEvent e) {
        if (e instanceof LifecycleApplicationEvent) {
            LifecycleApplicationEvent le = (LifecycleApplicationEvent) e;
            if (le.getEventType().equals(LifecycleApplicationEvent.CREATED) && le.objectIs(Album.class)) {
                if (albumTree != null) {
                    DefaultMutableTreeNode root = (DefaultMutableTreeNode) albumTreeModel.getRoot();
                    root.add(new DefaultMutableTreeNode(le.getObject()));
                    albumTreeModel.nodeStructureChanged(root);
                }
            }
        }
    }

    private JPopupMenu createAlbumPopupContextMenu() {
        // delete, addImage separator, properties
        CommandGroup group = getWindowCommandManager().createCommandGroup(
                "albumViewTableAlbumCommandGroup",
                new Object[]{"deleteCommand", "separator",
                        "propertiesCommand"});
        return group.createPopupMenu();
    }

    private class PropertiesExecutor extends AbstractActionCommandExecutor {
        public void execute() {
            if (getSelectedAlbum() != null) {
                albumPropertiesExecutor.execute();
            }
        }
    }

    private class DeleteExecutor extends AbstractActionCommandExecutor {
        public void execute() {
            int albumCount = 0;
            int imageCount = 0;
            int imageVersionCount = 0;
            final java.util.List<DefaultMutableTreeNode> nodesToDelete = new ArrayList<DefaultMutableTreeNode>();
            TreePath[] paths = albumTree.getSelectionPaths();
            for (TreePath path : paths) {
                DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
                if (node.isRoot()) {
                    continue;
                }
                if (node.getUserObject() instanceof Album) {
                    albumCount++;
                }
                nodesToDelete.add(node);
            }

            ConfirmationDialog dialog = new ConfirmationDialog() {
                protected void onConfirm() {
                    for (DefaultMutableTreeNode node : nodesToDelete) {
                        albumTreeModel.removeNodeFromParent(node);
                        if (node.getUserObject() instanceof Album) {
                            albumService.remove((Album) node.getUserObject());
                        }
                    }
                }
            };
            if (albumCount > 0) {
                dialog.setTitle(getMessage("confirmDeleteAlbumDialog.title"));
                dialog.setConfirmationMessage(getMessage("confirmDeleteAlbumDialog.label"));
            } else if (imageCount > 0) {
                dialog.setTitle(getMessage("confirmDeleteImageDialog.title"));
                dialog.setConfirmationMessage(getMessage("confirmDeleteImageDialog.label"));
            } else if (imageVersionCount > 0) {
                dialog.setTitle(getMessage("confirmDeleteImageVersionDialog.title"));
                dialog.setConfirmationMessage(getMessage("confirmDeleteImageVersionDialog.label"));
            }

            dialog.showDialog();
        }
    }

    // TODO: Move all usefull classes out of this mess!
    private class AlbumPropertiesExecutor extends AbstractActionCommandExecutor {

        private HierarchicalFormModel albumFormModel;

        private AlbumForm albumForm;

        private CompositeDialogPage compositePage;

        public void execute() {
            final Album album = getSelectedAlbum();
            albumFormModel = FormModelHelper.createCompoundFormModel(album);
            albumForm = new AlbumForm(FormModelHelper.createChildPageFormModel(albumFormModel, null));

            compositePage = new TabbedDialogPage("albumProperties");
            compositePage.addForm(albumForm);

            TitledPageApplicationDialog dialog = new TitledPageApplicationDialog(compositePage, getWindowControl()) {
                protected void onAboutToShow() {
                    albumForm.requestFocusInWindow();
                    setEnabled(compositePage.isPageComplete());
                }

                protected boolean onFinish() {
                    albumFormModel.commit();
                    albumService.saveOrUpdate(album);
                    albumTreeModel.nodeChanged(getSelectedAlbumNode());
                    return true;
                }
            };
            dialog.showDialog();
        }
    }

}